﻿using System;
using System.Collections.Generic;

namespace Tuxedo.Compile
{
	internal class Compiler
	{
		private CsProjData project;

		public Compiler(CsProjData projectData)
		{
			this.project = projectData;
		}

		public FPClass[] Compile()
		{
			List<FPClass> classes = new List<FPClass>();
			List<string> errors = new List<string>();
			List<AssemblyAttribute> assemblyAttributes = new List<AssemblyAttribute>();

			foreach (string key in this.project.CodeFiles.Keys)
			{
				try
				{
					string code = this.project.CodeFiles[key];
					FPClass[] fpClasses = this.CompileCode(key, code, assemblyAttributes);
					classes.AddRange(fpClasses);
				}
				catch (Exception e)
				{
					errors.Add(key + ": " + e.Message);
				}
			}

			if (errors.Count > 0)
			{
				throw new Exception("Errors encountered: " + errors.Count); // TODO: show them.
			}

			return classes.ToArray();
		}

		private FPClass[] CompileCode(string relativeFilename, string code, List<AssemblyAttribute> assemblyAttributes)
		{
			TokenStream tokens = new TokenStream(this.project, relativeFilename, code);

			List<UsingDirective> usings = new List<UsingDirective>();
			List<FPClass> classes = new List<FPClass>();

			while (tokens.IsNext("using"))
			{
				usings.Add(UsingDirective.Parse(tokens));
			}

			string next = tokens.PeekValue();

			while (next != null)
			{
				if (next == "[")
				{
					assemblyAttributes.Add(AssemblyAttribute.Parse(tokens));
				}
				else
				{
					this.ParseOutRootItems(null, tokens, classes);
				}
				next = tokens.PeekValue();
			}

			return classes.ToArray();
		}

		private void ParseOutRootItems(string fullyQualifiedNamespacePrefix, TokenStream tokens, List<FPClass> classes)
		{
			while (tokens.HasMore())
			{
				if (tokens.IsNext("}")) return;

				if (tokens.PopIfPresent("namespace"))
				{
					string namespacePath = tokens.PopIdentifier();
					while (tokens.PopIfPresent("."))
					{
						namespacePath += "." + tokens.PopIdentifier();
					}

					tokens.PopExpected("{");

					this.ParseOutRootItems(
						fullyQualifiedNamespacePrefix == null ? namespacePath : (fullyQualifiedNamespacePrefix + "." + namespacePath),
						tokens,
						classes);

					tokens.PopExpected("}");
				}
				else
				{
					FPClass clazz = FPClass.ParseClass(fullyQualifiedNamespacePrefix, tokens);
					classes.Add(clazz);
				}
			}
		}
	}
}
